﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Xml;
using System.Collections.ObjectModel;
using XamlLocalizer.Parsing;
using XamlLocalizer.Services;
using XamlLocalizer.Tools;
using XamlLocalizer.Views;

namespace XamlLocalizer.ViewModels
{
    public class PreviewViewModel
    {
        private readonly IVisualStudioAdapter visualStudioAdapter;
        private readonly IResourceHandler resourceHandler;

        private XamlExtractor xamlExtractor;
        private Dictionary<string, string> resKeys = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        private Dictionary<string, string> newKeys = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        private string keyAssemblyName;
        private string keyAssemblyResourceNamespace;
        private string keyResourceFile;
        private IResourceFileDesc selectedResourceFile;
        private string resClassName;

        public IList<IResourceFileDesc> ResourceFilesList { get; private set;}

        public IResourceFileDesc SelectedResourceFile
        {
            get { return selectedResourceFile; }
            set
            {
                selectedResourceFile = value;
                SelectedResourceFileChanged();
            }
        }

        public IList<PreviewItemViewModel> Items { get; private set; }

        public ICommand RunExtraction { get; private set; }

        public ICommand PrettyFormat { get; private set; }

        public ICommand FindCommand { get; private set; }

        public Window Window { get; set; }

        public IReplacementStrategy ReplaceMode { get; set; }

        public List<IReplacementStrategy> ReplaceModeList { get; set; }

        public bool ReformatXaml { get; set; }

        public PreviewViewModel(IVisualStudioAdapter visualStudioAdapter, IResourceHandler resourceHandler)
        {
            this.visualStudioAdapter = visualStudioAdapter;
            this.resourceHandler = resourceHandler;

            ReadXamlParseInfo();

            string xaml = visualStudioAdapter.GetActiveDocumentText();
            keyAssemblyName = visualStudioAdapter.GetActiveAssemblyName();
            xamlExtractor = new XamlExtractor(xaml);

            RunExtraction = new DelegateCommand(DoRunExtraction);
            PrettyFormat = new DelegateCommand(DoPrettyFormat);
            FindCommand = new DelegateCommand(DoFind);
            ReformatXaml = Properties.Settings.Default.ReformatXaml;

            FillReplaceModeList();

            FillItems();

            keyAssemblyResourceNamespace = resourceHandler.GetResourcesNamespace();
            FillResourceFiles();
        }

        private void ReadXamlParseInfo()
        {
            XamlExtractor.ReadXamlParseInfo(visualStudioAdapter.GetSolutionFolder());
        }

        private void FillResourceFiles()
        {
            ResourceFilesList = resourceHandler.GetResourceFileList();

            var propsDir = resourceHandler.GetResourcesFolder();
            var viewName = visualStudioAdapter.GetActiveDocumentName();
            var defResFileName = Path.Combine(propsDir, viewName + ".resx");

            var selResFile = ResourceFilesList.FirstOrDefault(
                r => String.Compare(r.FileName, defResFileName, StringComparison.OrdinalIgnoreCase) == 0);
            
            if (selResFile == null)
            {
                selResFile = resourceHandler.CreateResourceFileDesc(defResFileName);
                ResourceFilesList.Add(selResFile);
            }

            SelectedResourceFile = selResFile;
        }

        private void FillReplaceModeList()
        {
            ReplaceModeList = new List<IReplacementStrategy>();
            if (!visualStudioAdapter.IsActiveProjectSilverlight())
            {
                ReplaceModeList.Add(new LocXReplacement());
                ReplaceModeList.Add(new XStaticReplacement());
            }
            ReplaceModeList.Add(new BindingResourceWrapperReplacement(resourceHandler));

            string lastMode = Properties.Settings.Default.ReplaceMode;
            var replMode = ReplaceModeList.FirstOrDefault(r => r.Id == lastMode);
            if (replMode == null) replMode = ReplaceModeList[0]; 

            ReplaceMode = replMode;
        }

        public bool IsEmpty
        {
            get { return Items.Count == 0; }
        }


        private void SelectedResourceFileChanged()
        {
            resClassName = Path.GetFileNameWithoutExtension(SelectedResourceFile.FileName);
            FillResourceKeys();

            GenerateKeys();
        }

        private void FillItems()
        {
            Items = new ObservableCollection<PreviewItemViewModel>();
            foreach (var parseItem in xamlExtractor.ParseResult.NewItems)
            {
                Items.Add(new PreviewItemViewModel(parseItem));
            }
        }

        private void FillResourceKeys()
        {
            resKeys.Clear();

            var keys = SelectedResourceFile.GetAllKeys();
            if (keys != null)
            {
                foreach (var key in keys)
                {
                    resKeys[key] = null;
                }
            }
        }

        private void GenerateKeys()
        {
            newKeys.Clear();
            
            foreach (var item in Items)
            {
                if (String.IsNullOrEmpty(item.Key) || IsKeyInUse(item.Key))
                {
                    string key = xamlExtractor.GetWellKnownStringKey(item.Text);
                    item.Key = !String.IsNullOrEmpty(key) ? key : GetNewKey(item.Text);
                }
                else
                {
                    if (!IsFullKey(item.Key))
                    {
                        newKeys.Add(item.Key, null);
                    }
                }
            }
        }

        private static bool IsFullKey(string s)
        {
            return (s.IndexOf(':') >= 0);
        }

        private string GetNewKey(string s)
        {
            var sb = new StringBuilder();

            //bool sepAdded = true;
            // Capitalize first letter and after a space
            bool capitalize = true;
            for (int i = 0; i < s.Length; i++)
            {
                if (sb.Length == 0 && Char.IsDigit(s, i))
                {
                    sb.Append('S');
                }
                if (Char.IsLetterOrDigit(s, i))
                {
                    if (capitalize)
                    {
                        sb.Append(Char.ToUpper(s[i]));
                        capitalize = false;
                    }
                    else
                    {
                        sb.Append(s[i]);
                    }
                }
                else if (char.IsWhiteSpace(s, i))
                {
                    capitalize = true;
                }
                //else if (!sepAdded)
                //{
                //    sb.Append('_');
                //    sepAdded = true;
                //}
            }

            string rawKey = sb.ToString().TrimEnd('_');
            if (rawKey.Length > 30)
            {
                rawKey = rawKey.Substring(0, 30);
            }

            string key = rawKey;
            int ii = 1;
            while (IsKeyInUse(key))
            {
                key = rawKey + ii++;
            }
            newKeys.Add(key, null);
            return key;
        }

        private bool IsKeyInUse(string key)
        {
            return newKeys.ContainsKey(key) || resKeys.ContainsKey(key) || 
                String.Compare(key, resClassName, StringComparison.OrdinalIgnoreCase) == 0; //Key must be != class name
        }

        private void DoRunExtraction(object obj)
        {
            try
            {
                string lastMode = ReplaceMode.Id;
                Properties.Settings.Default.ReplaceMode = lastMode;
                Properties.Settings.Default.ReformatXaml = ReformatXaml;
                Properties.Settings.Default.Save();

                if (SelectedResourceFile.IsNew)
                {
                    resourceHandler.CreateNewResFile(SelectedResourceFile);
                }
                keyResourceFile = Path.GetFileNameWithoutExtension(SelectedResourceFile.FileName);

                AddNewKeys();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "XamlLocalizerAddin");
                throw;
            }
            finally
            {
                Window.Close();
            }
        }

        private void AddNewKeys()
        {
            bool changed = false;
            foreach (var item in Items)
            {
                if (item.IsChecked)
                {
                    ReplaceInXaml(item);

                    if (!IsFullKey(item.Key))
                    {
                        try
                        {
                            SelectedResourceFile.AddResource(item.Key, item.Text, item.Comment);
                        }
                        catch (ArgumentException ex)
                        {
                            if (!ex.Message.StartsWith("Resource already exists"))
                            {
                                throw;
                            }
                            //Reusing existing key - OK
                        }
                    }

                    changed = true;
                }
            }
            if (changed)
            {
                SaveChangedXaml();
                SelectedResourceFile.SaveFile();
            }
        }

        private void SaveChangedXaml()
        {
            ReplaceMode.PrepareReplacement(xamlExtractor, keyAssemblyResourceNamespace, keyResourceFile);

            string newXaml = GetNewDocumentXaml(ReformatXaml);
            visualStudioAdapter.SetActiveDocumentText(newXaml);
        }

        private string GetNewDocumentXaml(bool reformatXaml)
        {
            XmlDocument doc = xamlExtractor.Document;
            if (reformatXaml)
            {
                string xaml = doc.OuterXml;
                doc = new XmlDocument();
                doc.PreserveWhitespace = false;
                doc.LoadXml(xaml);
            }

            var wrt = PrettyXmlWriter.Create();
            doc.WriteTo(wrt);
            return wrt.GetXml();
        }

        private void ReplaceInXaml(PreviewItemViewModel item)
        {

            var keyDesc = IsFullKey(item.Key) ? 
                new ResourceKeyDesc(item.Key) :
                new ResourceKeyDesc(keyAssemblyName, keyResourceFile, item.Key);

            xamlExtractor.ReplaceInXaml(item.ParseItem, keyDesc, ReplaceMode);
        }

        private void DoPrettyFormat(object obj)
        {
            try
            {
                string newXaml = GetNewDocumentXaml(true);
                visualStudioAdapter.SetActiveDocumentText(newXaml);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "XamlLocalizerAddin");
                throw;
            }
            finally
            {
                Window.Close();
            }

        }

        private void DoFind(object obj)
        {
            var dlg = new FindUnlocalizedFilesDialog();
            var vm = new FindUnlocalizedFilesViewModel(visualStudioAdapter);
            dlg.Owner = Window;
            dlg.DataContext = vm;
            vm.Window = dlg;
            dlg.ShowDialog();
        }
    }
}
